<?php
/**
 * Hoffman Framework
 *
 * LICENSE
 *
 * This source file is subject to the GNU LGPL 2.1 license that is bundled
 * with this package in the file license.txt.
 * It is also available through the world-wide-web at this URL:
 * http://creativecommons.org/licenses/LGPL/2.1
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to annop@thaicyberpoint.com so we can send you a copy immediately.
 *
 * @package	   Hoffman
 * @author     Ford AntiTrust
 * @copyright  Copyright (c) 2008 Ford AntiTrust (http://www.thaicyberpoint.com/ford/blog)
 * @since	   Version Rv 0.2
 * @license    http://creativecommons.org/licenses/LGPL/2.1
 *             GNU LGPL 2.1
 * @filesource
 */

if (!defined('INDEX'))
    die('No direct script access allowed.');

/**
 * @see Zend_Registry
 */
require_once 'Zend/Registry.php';

/**
 * @see Zend_Cache
 */
require_once 'Zend/Cache.php';

/**
 * @see Hmf_Logs
 */
require_once 'Hmf/Logs.php';

/**
 * Routing
 *
 * Parse string from querystring to array of controller and action
 *
 * @package		Hoffman
 * @subpackage	Hmf
 * @category	Routing
 * @author		Ford AntiTrust
 * @version     SVN: $Id: Routing.php 148 2009-02-17 14:14:57Z annop.s@gmail.com $
 * @link		http://www.thaicyberpoint.com/ford/blog/hmf
 */

final class Hmf_Routing {

    /*
     * Querystring data
     */
    private $qs;

    /*
     * array from XML data
     */
    private $arrayXml;

    private $matchData;

    private $routes = array();

    private $rawPattern, $rawAction;

    private $arrayPattern, $arrayAction;

    function __construct(&$qs) {

        $this->routes = Zend_Registry::get('rounting');

        $app_config = Zend_Registry::get('app_config');

        $this->qs = rtrim($qs, '/');

        /* Empty the querystring */
        if (empty($this->qs)) {
            $this->matchData = array(':controller' => 'index', ':action' => 'index');
            return true;
        }

        /*  Split a 'qs' String to arrayQs variable by '/' */
        $arrayQs = explode('/', $this->qs);

        $this->routes['keywordslist_nodefault'] = $this->routes['keywords'];

        unset($this->routes['keywordslist_nodefault'][':controller'], $this->routes['keywordslist_nodefault'][':action']);

        $this->routes['keywordslist_nodefault'] = implode('|', array_keys($this->routes['keywordslist_nodefault']));

        if ($app_config['routing.cache']) {

            $app_cache = Zend_Registry::get('app_cache');

            // see if a cache already exists:
            if (!$dataCache = $app_cache->load('route')) {

                $this->generateRawAndArrayFromPatternAndActionFromRouteTable();

                $dataCache['arrayAction'] = $this->arrayAction;
                $dataCache['arrayPattern'] = $this->arrayPattern;
                $dataCache['rawAction'] = $this->rawAction;
                $dataCache['rawPattern'] = $this->rawPattern;

                if(Zend_Registry::get('is_production')) {
                    $app_cache->save($dataCache, 'route');
                }

            } else {

                $this->arrayAction = $dataCache['arrayAction'];
                $this->arrayPattern = $dataCache['arrayPattern'];
                $this->rawAction = $dataCache['rawAction'];
                $this->rawPattern = $dataCache['rawPattern'];

            }

        } else {

            $this->generateRawAndArrayFromPatternAndActionFromRouteTable();
        }

        foreach ($this->routes['map'] as $pattern => $action) {

            /* The querystring is equel the pattern */
            if (strcasecmp($this->qs, $pattern) === 0) {

                $this->matchData = array(':controller' => $this->arrayAction[$action][0], ':action' => (empty($this->arrayAction[$action][1]) ? 'index' : $this->arrayAction[$action][1]));

                $forShiftAction = $this->arrayAction[$action];

                array_shift($forShiftAction);
                array_shift($forShiftAction);

                if (count($forShiftAction) < 1) {
                    $this->matchData[':args'] = array();
                } else {
                    $this->matchData[':args'] = $forShiftAction;
                }

                return true;
            }
            /* Matched all the pattern in the String */
            if (preg_match('/^' . $this->rawPattern[$pattern] . '$/', $this->qs)) {
                if (empty($action)) { /* Matched for default pattern */

                    $this->matchData = array(':controller' => $arrayQs[0], ':action' => (empty($arrayQs[1]) ? 'index' : $arrayQs[1]));

                    array_shift($arrayQs); /* remove :controller */
                    array_shift($arrayQs); /* remove :action     */

                    $this->matchData[':args'] = $arrayQs;

                } else { /* Matched from the developer modified pattern */

                    $matches = null;

                    foreach ($this->arrayPattern[$pattern] as $k => $v) {
                        if (preg_match_all('/^:/', $v, $matches) > 0)
                            unset($arrayQs[$k]);
                    }

                    $varArgs = explode('/', str_ireplace(implode('/', $arrayQs) . '/', '', $this->qs));

                    $this->matchData = array(':controller' => $this->arrayAction[$action][0], ':action' => $this->arrayAction[$action][1], ':args' => $varArgs);

                }

                return true;
                /* Matched the prefix pattern in the String */
            } elseif (preg_match('/^' . $this->rawPattern[$pattern] . '/', $this->qs)) {

                /* Matched for the default pattern */
                if (empty($action)) {

                    $this->matchData = array(':controller' => $arrayQs[0], ':action' => (empty($arrayQs[1]) ? 'index' : $arrayQs[1]));

                    array_shift($arrayQs); /* remove :controller */
                    array_shift($arrayQs); /* remove :action     */

                    $this->matchData[':args'] = $arrayQs;

                    return true;
                }
            }
        }

        $this->matchData = array(':controller' => $arrayQs[0], ':action' => 'index');

        return false;
    }

    public final function getCurrentUrlAction() {

        return $this->qs;
    }

    public final function getRoutes() {

        return $this->matchData;
    }

    public final function getRoutesTable() {

        return $this->routes;
    }

    public final function getUrl() {

        $numArgs = func_num_args();

        if ($numArgs > 0) {
            $matches = null;
            $arrayUrl = func_get_args();
            $strUrl = implode('/', $arrayUrl);

            array_shift($arrayUrl); /* remove :controller    */
            array_shift($arrayUrl); /* remove :action        */

            foreach ($this->routes['map'] as $pattern => $action) {
                if (!empty($action) and preg_match('/^' . $this->rawAction[$action] . '$/', $strUrl)) {
                    if (preg_match_all('/(\/)?(' . $this->routes['keywordslist_nodefault'] . ')/', $action, $matches) > 0) {
                        foreach ($matches[0] as $key => $val) {
                            $pattern = str_replace($val, '/' . $arrayUrl[$key], $pattern);
                        }
                        return $pattern;
                    }
                }
            }

            return $strUrl;
        }

        return null;
    }

    public final function generateRawAndArrayFromPatternAndActionFromRouteTable() {

        foreach ($this->routes['map'] as $pattern => $action) {

            /*  Split a 'pattern|action' String to arrayPattern|arrayAction variable by '/' */
            $this->arrayAction[$action] = explode('/', $action);
            $this->arrayPattern[$pattern] = explode('/', $pattern);

            /* Replace regular expression from the keyword in the pattern|action to the rawPattern|rawAction variebles. */
            $this->rawAction[$action] = str_replace('/', '\/', $action);
            $this->rawPattern[$pattern] = str_replace('/', '\/', $pattern);

            foreach ($this->routes['keywords'] as $routesKeyword => $routesKeywordPattern) {
                $this->rawPattern[$pattern] = preg_replace('/' . $routesKeyword . '/', $routesKeywordPattern, $this->rawPattern[$pattern]);
                $this->rawAction[$action] = preg_replace('/' . $routesKeyword . '/', $routesKeywordPattern, $this->rawAction[$action]);
            }
        }
    }
}
?>